#pragma once

#include <_deps/crossguid/guid.hpp>

#include <UECS/Entity.h>

#include <unordered_map>
#include <filesystem>
#include <vector>
#include <regex>
#include <map>
#include <set>
#include <memory>

namespace Ubpa::Utopia {
	class Object;

	// ref: https://docs.unity3d.com/ScriptReference/AssetDatabase.html
	// asset: a file stored in hard disk
	// support
	// - basic: .lua, .hlsl, .shader, image(.jpg, .png, .bmp, .tga, .hdr), .tex2d, .texcube, .mat, .txt, .json, .scene
	// - model
	// * - support: .obj
	// * - optional (assimp): .ply
	// other as DefaultAsset except .meta (generated by AssetMngr, unimportable)
	class AssetMngr {
	public:
		static AssetMngr& Instance() {
			static AssetMngr instance;
			return instance;
		}

		// default : ".."
		const std::filesystem::path& GetRootPath() const noexcept;
		void SetRootPath(std::filesystem::path path);

		void Clear();

		bool IsImported(const std::filesystem::path& path) const;

		// Get the GUID for the asset at path.
		// If the asset does not exist, AssetPathToGUID will return invalid xg::Guid
		xg::Guid AssetPathToGUID(const std::filesystem::path& path) const;

		// unique
		bool CreateAsset(std::shared_ptr<Object> obj, const std::filesystem::path& path);
		template<typename Asset>
		bool CreateAsset(std::shared_ptr<Asset> ptr, const std::filesystem::path& path);
		// copy
		template<typename Asset>
		bool CreateAsset(Asset&& asset, const std::filesystem::path& path);

		bool Contains(const Object& obj) const;

		std::vector<xg::Guid> FindAssets(const std::wregex& matchRegex) const;

		const std::filesystem::path& GetAssetPath(const Object& obj) const;

		// empty xg::Guid is root
		const std::map<xg::Guid, std::set<xg::Guid>>& GetAssetTree() const;

		// get first asset type
		const std::type_info& GetAssetType(const std::filesystem::path&) const;

		// gets the corresponding asset path for the supplied guid, or an empty path if the GUID can't be found.
		const std::filesystem::path& GUIDToAssetPath(const xg::Guid&) const;

		// if not loaded, return nullptr
		std::shared_ptr<Object> GUIDToAsset(const xg::Guid&) const;
		std::shared_ptr<Object> GUIDToAsset(const xg::Guid&, const std::type_info&) const;
		template<typename T>
		std::shared_ptr<T> GUIDToAsset(const xg::Guid&) const;

		// import asset at path (relative)
		// * generate meta
		xg::Guid ImportAsset(const std::filesystem::path& path);
		// recursively import asset in directory (relative)
		// not import the 'directory'
		void ImportAssetRecursively(const std::filesystem::path& directory);

		// load first asset at path
		std::shared_ptr<Object> LoadAsset(const std::filesystem::path& path);
		// returns the first asset object of type at given path
		std::shared_ptr<Object> LoadAsset(const std::filesystem::path& path, const std::type_info&);
		// returns the first asset object of type at given path
		template<typename T>
		std::shared_ptr<T> LoadAsset(const std::filesystem::path& path);

		void ReserializeAsset(const std::filesystem::path& path);

		bool MoveAsset(const std::filesystem::path& src, const std::filesystem::path& dst);

		//bool DeleteAsset(const std::filesystem::path& path);

		// e.g. obj, lua
		bool IsSupported(std::string_view extension) const noexcept;

	private:
		struct Impl;
		Impl* pImpl;
		AssetMngr();
		~AssetMngr();
	};
}

#include "details/AssetMngr.inl"
